#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
TheCodeInn PyQt4 Tutorial
PyQt Calender App
Author: Peter Goldsborough
Website: http://thecodeinn.blogspot.com
last edited: August 2013
'''
import sys, pickle
from PyQt4 import QtGui, QtCore
#some global variables
dateStr = "" # will hold the date
name = "" # will hold the name of an added event
gridVar = 3 # will be used to extend the grid layout
sender = "" # will hold the sender button's text
s = "" # will hold the sender button itself
e = open("events.txt","rb") #this try - except clause is there to prevent loading errors if events.txt is empty
try:
events = pickle.loads(e.read())
except:
events = {}
e.close()
class Edit(QtGui.QDialog):
def __init__(self,parent = None):
QtGui.QDialog.__init__(self, parent)
#this is the dialog that opens up if a specific event is clicked
self.initUI()
def initUI(self):
global sender
self.change = QtGui.QLabel("Change: ",self)
self.change.move(5,7)
self.line = QtGui.QLineEdit(self)
self.line.move(65,5)
self.line.setText(sender)
self.delt = QtGui.QPushButton("Delete",self)
self.delt.move(80,50)
self.ok = QtGui.QPushButton("OK",self)
self.ok.move(40,100)
self.cancel = QtGui.QPushButton("Cancel",self)
self.cancel.move(120,100)
self.setGeometry(300,300,240,130)
self.setWindowTitle("Edit event")
self.setStyleSheet("font-size:14px;")
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.initUI()
def initUI(self):
self.widget = QtGui.QWidget(self)
self.cal = QtGui.QCalendarWidget(self)
self.cal.setGridVisible(True)
self.cal.clicked[QtCore.QDate].connect(self.showDate)
self.cal.setStyleSheet("font-size:15px")
self.upc = QtGui.QLabel(self)
self.upc.setStyleSheet("font-size:16px;")
self.Upcoming()
date = self.cal.selectedDate().toString()
self.lbl = QtGui.QLabel(date,self)
self.lbl.setStyleSheet("font-size:16px;")
self.add = QtGui.QPushButton("+ Add",self)
self.add.clicked.connect(self.AddEvent)
self.grid = QtGui.QGridLayout()
self.spacer = QtGui.QSpacerItem(20, 30, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
self.grid.addWidget(self.cal,0,0,1,2)
self.grid.addItem(self.spacer,2,0)
self.grid.addWidget(self.upc,1,0)
self.grid.addWidget(self.lbl,3,0)
self.grid.addWidget(self.add,3,1)
self.widget.setLayout(self.grid)
#---------Window settings --------------------------------
self.setGeometry(300,300,400,400)
self.setWindowTitle("PyCal")
self.setCentralWidget(self.widget)
#---------Slot functions --------------------------------
def Upcoming(self):
#this function takes care of displaying a list of upcoming events
count = 0
length = 0
upcoming = "Upcoming events: "
if events: #if events is not empty
for i in events.values(): #for every list of events
for j in i: # for every event in these lists
length += 1
while count < 2: #set it to a higher number if you want more items displayed
for i in events.values():
for j in i:
if length < 2:
upcoming += j #if there is only one event, we append it to the string and immediately exit the while loop, without this it'd display an event twice if there was only one
count = 2
else:
upcoming += j+", "
count += 1
else:
upcoming += "None" #if there are no events, we display "None"
self.upc.setText(upcoming)
def Edit(self):
# this function is called when an event is clicked, opening a QDialog that lets you edit the name or delete it
global sender
global s
global events
s = self.sender() #get the information from the sending button so we know which one it is, since they're generically created
sender = s.text()
edit = Edit(self) #modularly open the Edit QDialog
edit.show()
def Ok():
#if the name is changed, we save the new name and delete the old one
events[dateStr].remove(s.text())
events[dateStr].append(edit.line.text())
e = open("events.txt","wb")
pickle.dump(events,e)
e.close()
s.setText(edit.line.text())
self.Upcoming()
edit.close()
def Delete():
#if it is deleted, we have to delete it from the layout, this is done by that for loop down there
for i in reversed(range(self.grid.count())): #the reversed is there since it'd mess up the layout otherwise
try:
if self.grid.itemAt(i).widget().text() == sender:
self.grid.itemAt(i).widget().setParent(None)
except:
#you may wonder why I have a try - except clause here if I'm not doing anything when there's an exception, well, that's because the spacer
#is a QItem, and that would mess up the self.grid.itemAt(i).widget() up there, since the spacer is an item, not a widget
pass
if len(events[dateStr]) > 1: #if there are more than one values for that day, we just remove this event
events[dateStr].remove(sender)
else: #if this was the day's last event we just deleted, it's good to delete the whole list, empty lists suck
del events[dateStr]
e = open("events.txt","wb")
pickle.dump(events,e)
e.close()
self.Upcoming() #update the upcoming events
edit.close()
edit.delt.clicked.connect(Delete)
edit.cancel.clicked.connect(lambda cancel: edit.close())
edit.ok.clicked.connect(Ok)
def showDate(self, date):
global dateStr
global events
global gridVar
#from here
eventStr = ""
gridVar = 3
for i in reversed(range(self.grid.count())):
try:
self.grid.itemAt(i).widget().setParent(None)
except:
pass
self.grid.addWidget(self.cal,0,0,1,2)
self.grid.addItem(self.spacer,2,0)
self.grid.addWidget(self.upc,1,0)
self.grid.addWidget(self.lbl,3,0)
self.grid.addWidget(self.add,3,1)
self.setGeometry(300,300,400,400)
#to here, we just reset the whole layout to the default - no events yet
dateStr = date.toString()
if dateStr in events: #now we get funky, so if the date is in the events dictionary
for i in events[dateStr]: #we create a button for every event
b = QtGui.QPushButton(i,self)
b.setStyleSheet("border-radius:5px;font-size:16px;")
b.clicked.connect(self.Edit)
'''
If you wonder why the buttons aren't displayed like buttons, but like labels, yet
still work like buttons (clickable), that's because I set the border radius to 1px
up there in the StyleSheet. QPushButton doesn't seem to support border-radius modification
so it just displays the button flat .. neat trick if you want a clickable qlabel!
'''
self.grid.addWidget(b,gridVar,1) #extend the layout
self.grid.addWidget(self.add,gridVar+1,1)
gridVar +=1
self.lbl.setText(dateStr)
def AddEvent(self):
global dateStr
global events
global date
name, ok = QtGui.QInputDialog.getText(self,"Add event", #here we call an input dialog to get the event's name
"Name: ")
if dateStr not in events: #if the date isn't in the events list yet, we create a new dictionary item
events[dateStr] = [name]
else: #if it is, we append it to the date's events list
events[dateStr].append(name)
e = open("events.txt","wb")
pickle.dump(events,e)
e.close()
self.showDate(self.cal.selectedDate())
self.Upcoming()
def main():
app = QtGui.QApplication(sys.argv)
main= Main()
main.show()
print("I \N{black heart suit} Python")
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Hope you like the easter egg in my program :)
No comments :
Post a Comment