PyQt Application Main Loop?

Post here if you need help with creating a Graphical User Interface in Python.

PyQt Application Main Loop?

Postby Yiab » Fri Apr 04, 2014 2:54 pm

I'm not getting any errors, but my code still isn't working.
I'm trying to write a small email checking client which will pop up a window in the lower right corner when there's new mail, and where the window will go away when it is clicked on.

Code: Select all
def main():
   app=QApplication(argv)
   window=NotifyWindow()
   runner=Thread(target=keep_checking_gmail,args=[window])
   runner.start()
   exit(app.exec_())


So NotifyWindow is just a QWidget containing a single button which holds the text I want to display. When I test it out in the shell, it displays and vanishes when it should, with the text it should. keep_checking_gmail does what its name suggests, and it tells the NotifyWindow when and what to display. When I run this main method, it all seems to work correctly until it actually gets its first new email registered, then the NotifyWindow tries to show up, but never actually gets painted to the screen (it's all white) and I get an hourglass when I hover the pointer over the NotifyWindow. This continues (with the thread still chugging along in the background) until I kill the process or the program simply exits without an error message.

While I don't really know where in my code the problem is, I'm displaying the main method since that's almost the only thing that's run outside of the keep_checking_gmail function. Any help would be appreciated.

I'm using PyQt5 for Python 3.3 and Windows 7.

Note: I am terrible at GUI programming in any language. Please forgive me if this is a really stupid error on my part.
Last edited by stranac on Fri Apr 04, 2014 2:57 pm, edited 1 time in total.
Reason: First post lock.
Yiab
 
Posts: 3
Joined: Fri Apr 04, 2014 2:41 pm

Re: PyQt Application Main Loop?

Postby Mekire » Fri Apr 04, 2014 5:11 pm

First and foremost. Stop using star imports. Just in those three lines it is clear you have star imported at least 3 modules. This truly makes your code unreadable for anyone that doesn't know all modules you are using inside out.

Secondly, you should post more of your program. It should be a testable sample. If this requires you to post your whole program, so be it.
It seems your main problem is you never called window.show() but that is my only guess for the moment.

-Mek
User avatar
Mekire
 
Posts: 1015
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: PyQt Application Main Loop?

Postby Yiab » Fri Apr 04, 2014 7:37 pm

You are correct that I'm using star imports, though this is because I am totally unfamiliar with PyQt and have copied the basic structure from which I'm working from some sample code found through Google.

The main reason I didn't post the full code is because I read on the "what to do/what not to do" for these forums that it should be a minimal amount of code. But anyway, here it is.

Code: Select all
from usblamp import USBLamp, Color
from imaplib import IMAP4_SSL
from pickle import load, dump
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from time import sleep
from sys import argv, exit
from threading import Thread, RLock, Event
import ctypes, ctypes.wintypes

CREDENTIAL_FILE="login_info.pickle"
SEEN_EMAIL_FILE="seen_emails.pickle"
IDLE_CHECK_INTERVAL=10
GMAIL_CHECK_PER_IDLE_CHECK=6
IDLE_TIME=180
__stop_var=Event()

class NotifyWindow(QWidget):
   def __init__(self,parent=None):
      super(NotifyWindow,self).__init__(parent)
      self.win_lock=RLock()
      self.mainLabel=QPushButton("")
      self.mainLabel.clicked.connect(self.wasClicked)
      mainLayout=QGridLayout()
      mainLayout.addWidget(self.mainLabel,0,1)
      self.setLayout(mainLayout)
      self.showing_mail=[]
      self.is_showing=False
      self.setWindowFlags(Qt.FramelessWindowHint)
      self.setFocusPolicy(Qt.NoFocus)
      self.setContentsMargins(-10,-10,-10,-10)
      self.is_idle=False
      self.lamp=USBLamp()
      self.lamp.open()
      self.lamp.prepare()

   def show_window(self):
      if not self.is_idle:
         self.lamp.set_color(Color("blue"))
      self.show()
      self.is_showing=True

   def unset_idle(self):
      with self.win_lock:
         self.is_idle=False
         if self.is_showing:
            self.lamp.set_color(Color("blue"))

   def set_idle(self):
      with self.win_lock:
         self.is_idle=True
         self.lamp.switch_off()

   def wasClicked(self):
      with self.win_lock:
         self.hide()
         self.is_showing=False
         self.lamp.switch_off()
         self.showing_mail=[]

   def update_text(self,text):
      with self.win_lock:
         self.showing_mail.append(text)
         self.hide()
         self.mainLabel.setText("\r\n".join(self.showing_mail))
         self.show_window()
         dt=QDesktopWidget().availableGeometry()
         tw=self.frameGeometry()
         self.move(dt.width()-tw.width(),dt.height()-tw.height())

def get_login_info():
   return load(open(CREDENTIAL_FILE,"rb"))

def get_seen_emails():
   return load(open(SEEN_EMAIL_FILE,"rb"))

def set_seen_emails(seen_emails):
   dump(seen_emails,open(SEEN_EMAIL_FILE,"wb"))

def check_gmail(window=None):
   imap_server=IMAP4_SSL("imap.gmail.com",993)
   imap_server.login(*get_login_info())
   imap_server.select('INBOX')
   status,email_ids_raw=imap_server.uid('search',None,'(UNSEEN)')
   email_ids=email_ids_raw[0].split()
   old_email_ids=get_seen_emails()
   for eid in email_ids:
      if eid not in old_email_ids:
         _,ans=imap_server.uid('fetch',eid,'(BODY.PEEK[HEADER.FIELDS (From Subject)] RFC822.SIZE)')
         if window!=None:
            window.update_text(str(ans[0][1]))
   old_email_ids|=set(email_ids)
   set_seen_emails(old_email_ids)
   print("step 0")
   imap_server.logout()

def stop():
   __stop_var.set()

def should_stop():
   return __stop_var.is_set()

class LASTINPUTINFO(ctypes.Structure):
   _fields_ = [
      ('cbSize', ctypes.wintypes.UINT),
      ('dwTime', ctypes.wintypes.DWORD),
      ]

PLASTINPUTINFO = ctypes.POINTER(LASTINPUTINFO)
user32 = ctypes.windll.user32
GetLastInputInfo = user32.GetLastInputInfo
GetLastInputInfo.restype = ctypes.wintypes.BOOL
GetLastInputInfo.argtypes = [PLASTINPUTINFO]
kernel32 = ctypes.windll.kernel32
GetTickCount = kernel32.GetTickCount
Sleep = kernel32.Sleep

def keep_checking_gmail(window=None):
   liinfo=LASTINPUTINFO()
   liinfo.cbSize=ctypes.sizeof(liinfo)
   is_idle=False
   counter=0
   while not should_stop():
      if counter==0:
         check_gmail(window)
      print("step 1")
      GetLastInputInfo(ctypes.byref(liinfo))
      elapsed=GetTickCount()-liinfo.dwTime
      print("step 2")
      if is_idle:
         if elapsed>IDLE_TIME*1000:
            is_idle=True
            if window!=None:
               window.set_idle()
      elif elapsed<IDLE_TIME*1000:
         is_idle=False
         if window!=None:
            window.unset_idle()
      counter=(counter+1)%GMAIL_CHECK_PER_IDLE_CHECK
      print("step 3")
      sleep(IDLE_CHECK_INTERVAL)

def main():
   app=QApplication(argv)
   window=NotifyWindow()
   runner=Thread(target=keep_checking_gmail,args=[window])
   runner.start()
   exit(app.exec_())

def main_thread():
   runner=Thread(target=main)
   runner.start()

if __name__=="__main__":
   main()


As you can see, window.show() is called via the thread I started in the main method, when there's email to actually show.
Yiab
 
Posts: 3
Joined: Fri Apr 04, 2014 2:41 pm

Re: PyQt Application Main Loop?

Postby Yiab » Tue Apr 08, 2014 5:11 pm

Okay, here's a much smaller example which fails in the same way and includes all the package names.

Code: Select all
import PyQt5.QtWidgets
import PyQt5.QtCore
import PyQt5.QtGui
import sys
import time
import threading

__stop_var=threading.Event()

class ButtonWindow(PyQt5.QtWidgets.QWidget):
   def __init__(self,parent=None):
      super(ButtonWindow,self).__init__(parent)
      self.button=PyQt5.QtWidgets.QPushButton("Push This Button")
      self.button.clicked.connect(self.wasClicked)
      mainLayout=PyQt5.QtWidgets.QGridLayout()
      mainLayout.addWidget(self.button,0,1)
      self.setLayout(mainLayout)

   def wasClicked(self):
      self.hide()

def show_window(window=None):
   while not __stop_var.is_set():
      if window!=None:
         window.show()
      time.sleep(10)

def main():
   app=PyQt5.QtWidgets.QApplication(sys.argv)
   window=ButtonWindow()
   runner=threading.Thread(target=show_window,args=[window])
   runner.start()
   exit(app.exec_())

if __name__=="__main__":
   main()


I assume that I'm doing something that's obviously stupid, but I can't see what it might be. Can anybody help?
Yiab
 
Posts: 3
Joined: Fri Apr 04, 2014 2:41 pm


Return to GUI

Who is online

Users browsing this forum: dnallov, metulburr and 3 guests