2017-01-22 22 views
0

내 NumLk이 켜져 있거나 꺼져있는 경우 컴퓨터에서 내 NumLk이 켜져 있는지 여부를 알려주지 않으므로 내 시스템 트레이에 아이콘에 따라 변경됩니다. 내 NumLk의 상태. 이 .py는 컴퓨터가 켜져있을 때 항상 실행됩니다.NumLk 상태를 반영하는 시스템 트레이에 아이콘을 표시하는 방법

지금까지 3 코드를 혼합 할 수 있었고 아이콘을 시스템 트레이에 표시 할 수 있었지만 NumLk 상태가 변경되면 업데이트되지 않습니다. 내가 두 번 NumLk 키를 누르면 사실, 난 여전히 같은 아이콘합니다 (on 하나)를 얻을이 오류가 얻을 :

QCoreApplication::exec: The event loop is already running 
    File "\systray_icon_NumLk_on_off.py", line 21, in on_key_press 
    main(on) 
    File "\systray_icon_NumLk_on_off.py", line 46, in main 
    sys.exit(app.exec_()) 
SystemExit: -1 

내 코드가되지 않을 수도 있습니다 그것을 할 수있는 최선의 방법을, 그래서 어떤 대안을 환영합니다! 두 번 app.run()을 시작하려고하고 있기 때문에

#####get the state of NumLk key 
from win32api import GetKeyState 
from win32con import VK_NUMLOCK 
#how to use: print(GetKeyState(VK_NUMLOCK)) 
#source: http://stackoverflow.com/questions/21160100/python-3-x-getting-the-state-of-caps-lock-num-lock-scroll-lock-on-windows 

#####Detect if NumLk is pressed 
import pyglet 
from pyglet.window import key 
window = pyglet.window.Window() 
#source: http://stackoverflow.com/questions/28324372/detecting-a-numlock-capslock-scrlock-keypress-keyup-in-python 

on=r'on.png' 
off=r'off.png' 

@window.event 
def on_key_press(symbol, modifiers): 
    if symbol == key.NUMLOCK: 
     if GetKeyState(VK_NUMLOCK): 
      #print(GetKeyState(VK_NUMLOCK))#should be 0 and 1 but 
      main(on) 
     else: 
      main(off) 
@window.event 
def on_draw(): 
    window.clear() 

### display icon in systray 
import sys 
from PyQt5 import QtCore, QtGui, QtWidgets 
#source: http://stackoverflow.com/questions/893984/pyqt-show-menu-in-a-system-tray-application - add answer PyQt5 
class SystemTrayIcon(QtWidgets.QSystemTrayIcon): 

    def __init__(self, icon, parent=None): 
     QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) 
     menu = QtWidgets.QMenu(parent) 
     exitAction = menu.addAction("Exit") 
     self.setContextMenu(menu) 

def main(image): 
    app = QtWidgets.QApplication(sys.argv) 

    w = QtWidgets.QWidget() 
    trayIcon = SystemTrayIcon(QtGui.QIcon(image), w) 

    trayIcon.show() 
    sys.exit(app.exec_()) 

if __name__ == '__main__': 
    pyglet.app.run() 
+1

라이브러리를 과도하게 사용하는 이유는 무엇입니까? Qt는 Pyglet처럼 키 누르기를 확인할 수 있습니다. 또한 win32api 수 있습니다 .. 그럼 왜 spagetti - 논리? – Torxed

답변

1

QCoreApplication::exec: The event loop is already running 그 이유는 실제로 : 여기에 지금까지 해낸 것입니다. Qt는 이미 인스턴스가 실행 중임을 확인하고이 예외를 throw합니다. 대신에, 당신이하고 싶은 것은 이미 실행중인 인스턴스의 아이콘을 바꾸는 것입니다.

여기서 가장 중요한 문제는 나에게 묻는다면 하나의 작업을 해결하기위한 라이브러리의 혼합입니다.
두 가지 작업이 있지만 그래픽 부분에 Qt5를 사용하면 문제가 없습니다.

Pyglet을 사용하는 방법은 나가는 것이 잘못되었습니다.
Pyglet은 그래픽 엔진을 빌드 할 때 매우 강력하고 효과적인 그래픽 라이브러리입니다. 예를 들어 게임이나 비디오 플레이어 등을 제작하는 경우.

win32api를 사용하는 방식은 그래픽 창에서 키를 눌렀을 때만 값을 확인하기 때문에 잘못되었습니다.

win32api 코드를 스레드 (정확한 QtThread)로 옮기면 그래픽 창에서 키를 눌렀는지 여부에 관계없이 상태를 확인할 수 있습니다.

import sys 
import win32api 
import win32con 
from PyQt5 import QtCore, QtGui, QtWidgets 
from threading import Thread, enumerate 
from time import sleep 

class SystemTrayIcon(QtWidgets.QSystemTrayIcon): 
    def __init__(self, icon, parent=None): 
     QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) 
     menu = QtWidgets.QMenu(parent) 
     exitAction = menu.addAction("Exit") 
     exitAction.setShortcut('Ctrl+Q') 
     exitAction.setStatusTip('Exit application') 
     exitAction.triggered.connect(QtWidgets.qApp.quit) 
     self.setContextMenu(menu) 

class KeyCheck(QtCore.QThread): 
    def __init__(self, mainWindow): 
     QtCore.QThread.__init__(self) 
     self.mainWindow = mainWindow 

    def run(self): 
     main = None 
     for t in enumerate(): 
      if t.name == 'MainThread': 
       main = t 
       break 

     while main and main.isAlive(): 
      x = win32api.GetAsyncKeyState(win32con.VK_NUMLOCK) 
      ## Now, GetAsyncKeyState returns three values, 
      ## 0 == No change since last time 
      ## -3000/1 == State changed 
      ## 
      ## Either you use the positive and negative values to figure out which state you're at. 
      ## Or you just swap it, but if you just swap it you need to get the startup-state correct. 
      if x == 1: 
       self.mainWindow.swap() 
      elif x < 0: 
       self.mainWindow.swap() 
      sleep(0.25) 

class GUI(): 
    def __init__(self): 
     self.app = QtWidgets.QApplication(sys.argv) 

     self.state = True 

     w = QtWidgets.QWidget() 
     self.modes = { 
      True : SystemTrayIcon(QtGui.QIcon('on.png'), w), 
      False : SystemTrayIcon(QtGui.QIcon('off.png'), w) 
     } 

     self.refresh() 

     keyChecker = KeyCheck(self) 
     keyChecker.start() 

     sys.exit(self.app.exec_()) 

    def swap(self, state=None): 
     if state is not None: 
      self.state = state 
     else: 
      if self.state: 
       self.state = False 
      else: 
       self.state = True 
     self.refresh() 

    def refresh(self): 
     for mode in self.modes: 
      if self.state == mode: 
       self.modes[mode].show() 
      else: 
       self.modes[mode].hide() 

GUI() 

자주 (4 년마다) Qt 프로그래밍을 수행하지 않습니다.
그래서이 코드는 버그가 많습니다. Ctrl + C +를 눌러야합니다. 메뉴에서 "종료"를 눌러 중지하십시오.

솔직히 Qt에서 스레드를 관리하는 방법이나 응용 프로그램을 올바르게 종료하는 방법을 배우는 데 더 많은 시간과 노력을 들이고 싶지는 않습니다. 내 전문 분야가 아닙니다. 그러나 이것은 여러분이했던 main() 루프를 다시 시작하는 대신 아이콘을 하단 구석에 어떻게 바꿀 수 있는지에 대한 실용적인 예제를 줄 것입니다.

+0

고마워요. @Torxed. 이것은 내가 찾고 있었던 바로 그 것이었다. spagetti-logic은 파이썬에 익숙하지 않아 비 관련 게시물의 코드를 복사했기 때문입니다. 이제 주문을 했으니 곧은 길을 따라 가야합니다! 시간 내 주셔서 감사합니다. – Enora

+0

@ 에노라 나의 기쁨. 나는 당신이했던 것과 똑같은 방식으로 시작했다. 여기저기서 코드를 날치기 만하면 그것들을 모아서 하나의 스파게티로 만들었다. 그것은 학습 도구로 작동하므로 계속 사용하십시오. 모험심과 행운을 빕니다. 첫 번째 비트가 작동합니다. – Torxed