Related
Classes.py
from tkinter import *
BGCL = '#000000'
CANCELCL = '#800000'
CANCELHOVCL = '#400000'
INFOCL = '#000080'
INFOHOVCL = '#000040'
BARCL = '#004000'
STROKECL = '#00c000'
class FantomForm(Tk):
def __init__(self):
Tk.__init__(self)
self.withdraw()
class MainForm(Tk):
def __init__(self):
Tk.__init__(self)
self['bg'] = BGCL
self.overrideredirect(True)
self.bar = Frame(self,bg = BARCL)
self.bar.place(x = 0 ,y = 0,relwidth = 1,height = 24)
self.closebtn = Button(self,bg = CANCELCL,fg = BGCL,relief = FLAT,command = self.Exit,bd=0,activebackground = BGCL)
#print(self.winfo_reqwidth())
self.closebtn.place(width =24,height = 24,x = self.winfo_reqwidth()-24)
self.bar.bind("<ButtonPress-1>", self.StartMove)
self.bar.bind("<ButtonRelease-1>", self.StopMove)
self.bar.bind("<B1-Motion>", self.OnMotion)
self.closebtn.bind("<Enter>",self.__closebtne)
self.closebtn.bind("<Leave>",self.__closebtnl)
def __closebtne(self,event = None):
self.closebtn['bg'] = CANCELHOVCL
def __closebtnl(self,event = None):
self.closebtn['bg'] = CANCELCL
def StartMove(self, event = None):
self.dragx = event.x
self.dragy = event.y
def StopMove(self, event = None):
self.dragx = None
self.dragy = None
def OnMotion(self, event = None):
deltax = event.x - self.dragx
deltay = event.y - self.dragy
x = self.winfo_x() + deltax
y = self.winfo_y() + deltay
self.geometry("+%s+%s" % (x, y))
def Exit(self,event = None):
self.destroy()
class Form(Toplevel):
def __init__(self,master = None):
Toplevel.__init__(self,master)
self['bg'] = BGCL
self.overrideredirect(True)
self.bar = Frame(self,bg = BARCL)
self.bar.place(x = 0 ,y = 0,relwidth = 1,height = 24)
self.closebtn = Button(self,bg = CANCELCL,fg = BGCL,relief = FLAT,command = self.Exit,bd=0,activebackground = BGCL)
#print(self.winfo_reqwidth())
self.closebtn.place(width =24,height = 24,x = self.winfo_reqwidth()-24)
self.bar.bind("<ButtonPress-1>", self.StartMove)
self.bar.bind("<ButtonRelease-1>", self.StopMove)
self.bar.bind("<B1-Motion>", self.OnMotion)
self.closebtn.bind("<Enter>",self.__closebtne)
self.closebtn.bind("<Leave>",self.__closebtnl)
def __closebtne(self,event = None):
self.closebtn['bg'] = CANCELHOVCL
def __closebtnl(self,event = None):
self.closebtn['bg'] = CANCELCL
def StartMove(self, event = None):
self.dragx = event.x
self.dragy = event.y
def StopMove(self, event = None):
self.dragx = None
self.dragy = None
def OnMotion(self, event = None):
deltax = event.x - self.dragx
deltay = event.y - self.dragy
x = self.winfo_x() + deltax
y = self.winfo_y() + deltay
self.geometry("+%s+%s" % (x, y))
def Exit(self,event = None):
self.destroy()
Main.py
from Classes import *
Form0 = MainForm()
Form1 = Form(MainForm)
Form0.mainloop()
Form1.mainloop()
MainForm is class based on Tk
Form is class based on Toplevel
The master for a widget must be another widget. You're passing a class, and a class isn't a widget. You can use an instance of the class, however.
Instead of this:
Form0 = MainForm()
Form1 = Form(MainForm)
do this:
Form0 = MainForm()
Form1 = Form(Form0)
Also, you should only call mainloop once. You shouldn't call it for every window.
In the ShowMoneyButton class, I want the activation function to only activate if self.button_activated is False, and I set it as False when the SongButton class's activation function is triggered, yet it's not registering. It stays True. Is there a way I can make it so the ShowMoneyButton class's activation function is executed only if the SongButton class's activation function is executed, except for the first time?
import tkinter as tk, random, subprocess, time, os, datetime, pprint
class Song:
def __init__(self):
self.files = []
self.path = "/users/eliasrahoui/desktop/songy"
for filename in os.listdir(self.path):
self.files.append(self.path + "/" + filename)
class Money_Value:
def __init__(self):
with open("money_counter.txt", "r") as f:
a = f.read()
a = int(a)
self.current_value = a
class Clock:
def __init__(self, root):
money = Money_Value()
self.money = money.current_value
self.root = root
self.label = tk.Label(self.root, text = "$" + str(self.money), foreground = "green", font = ('calibri', 100, 'bold'))
self.label.pack()
def countup(self, *args):
root = self.root
money_per_hour = 1000
self.money += 1
self.label['text'] = "$" + str(self.money)
self.root_id = root.after(money_per_hour, self.countup)
def stop(self):
root = self.root
root.after_cancel(self.root_id)
with open("money_counter.txt", "w") as f:
f.write(str(self.money))
def save(self):
with open("money_counter.txt", "w") as f:
f.write(str(self.money))
self.root.destroy()
class Button:
all_buttons = {}
windows = []
return_code = []
button_activated = False
def __init__(self, root, text, command):
self.text = text
button = tk.Button(root, text = text, command = command)
self.all_buttons[self.text] = button
def remove(self, button):
button.pack_forget()
def show_button(self, button):
button.pack(side ="bottom")
class SongButton(Button):
def __init__(self, root, clock):
self.root = root
self.root_clock = clock
super().__init__(self.root, text = "stop song", command = self.activation)
def activation(self):
windows = self.windows
for i in windows:
i.destroy()
code = self.return_code[-1]
code.kill()
self.remove(self.all_buttons["stop song"])
self.root_clock.countup()
self.button_activated = False
class ShowMoneyButton(Button):
def __init__(self, root, clock):
self.root = root
super().__init__(self.root, "show me the money", self.activation)
song = Song()
self.songs = song.files
money_show_button = self.all_buttons["show me the money"]
self.show_button(money_show_button)
self.pic = tk.PhotoImage(file = "/users/eliasrahoui/desktop/IMG_7465.png")
#self.button_activated = False
self.label = tk.Label(self.root, text = "")
self.root_clock = clock
def activation(self):
if self.button_activated == False:
self.root_clock.stop()
num = Money_Value()
money = num.current_value
self.label.config(text =f'Money {money}')
self.label.pack()
random_song = random.choice(self.songs)
a = SongButton(self.root, self.root_clock)
return_cod = subprocess.Popen(["afplay", random_song])
self.return_code.append(return_cod)
self.show_button(self.all_buttons["stop song"])
self.button_activated = True
for _ in range(30):
root = self.root
window = tk.Toplevel(self.root)
self.windows.append(window)
window.minsize(100, 100)
window_label = tk.Label(window, image = self.pic)
window_label.pack()
ws = root.winfo_screenwidth() # width of the screen
hs = root.winfo_screenheight() # height of the screen
x = random.randint(0, ws)
y = random.randint(0, hs)
w = 200
h = 200
window.geometry('%dx%d+%d+%d' % (w, h, x, y))
class Timer(Button):
def __init__(self, root, start_time, name, counter = False):
self.count_down_start_time = start_time
self.root = root
self.name = name
self.counter = counter
self.tmp_counter = counter
super().__init__(self.root, text = name, command = self.timer_window)
self.show_button(self.all_buttons[self.name])
self.timer_id = None
self.window_label = None
self.orignial = start_time
self.window = None
self.current_question = 1
self.questions_answered = {}
self.times = [self.orignial]
self.positive = False
self.min_max = {}
def timer_window(self):
self.window = tk.Toplevel(self.root)
self.window.geometry('%dx%d+%d+%d' % (300, 200, 500, 300))
self.window_label = tk.Label(self.window, text = "", font = ('calibri', 100, 'bold'))
self.counter_label = tk.Label(self.window, text = f'Current Question: {self.current_question}')
self.window_label.pack()
self.timer()
def timer(self):
mins, secs = divmod(self.count_down_start_time, 60)
mins = abs(mins)
secs = abs(secs)
timerr = '{:02d}:{:02d}'.format(mins, secs)
if self.count_down_start_time == -1:
self.positive = True
self.window_label["foreground"] = "red"
if self.positive:
self.count_down_start_time += 1
else:
self.count_down_start_time -= 1
self.window_label["text"] = timerr
self.window.protocol("WM_DELETE_WINDOW", self.callback)
if self.counter == True:
button = tk.Button(self.window, text = "Question Done!", command = self.counter_func)
button.pack(side = "bottom")
button_report = tk.Button(self.window, text = "Report", command = self.report)
self.counter = False
self.counter_label.pack()
button_report.pack(side = "bottom")
self.timer_id = self.window.after(1000, self.timer)
def callback(self):
self.window.after_cancel(self.timer_id)
self.window.destroy()
self.count_down_start_time = self.orignial
if self.tmp_counter == True:
self.counter = True
self.current_question = 1
self.questions_answered = {}
self.positive = False
self.times = [self.orignial]
def counter_func(self):
self.questions_answered[self.current_question] = str(datetime.timedelta(seconds = self.times[-1] - self.count_down_start_time))
self.times.append(self.count_down_start_time)
self.current_question += 1
self.counter_label["text"] = f'Current Question: {self.current_question}'
def report(self):
vals = self.times_min_max()
new_window = tk.Toplevel(self.window)
new_window.minsize(300,300)
a = self.questions_answered[1]
data_pretty = pprint.pformat(self.questions_answered)
label = tk.Label(new_window, text = data_pretty)
label.pack()
mx = {f'Maximum time took per question: {vals[0]} question numbers': vals[2]}
mn = {f'Minimum time took per question: {vals[1]} question numbers': vals[-1]}
mx_label = tk.Label(new_window, text = mx, font = ('calibri', 14, 'bold'))
mn_label = tk.Label(new_window, text = mn, font = ('calibri', 14, 'bold'))
mn_label.pack(side = "bottom")
mx_label.pack(side = "bottom")
def times_min_max(self):
f = {}
big = []
small = []
for i in range(1, len(self.questions_answered) + 1):
val = self.questions_answered[i]
if ":" in val:
q = val.split(":")
secs = int(q[-1])
mins = q[0]
secs = secs/60
secs = str(secs)
secs = secs.split(".")
secs = secs[-1]
final = mins + "." + secs
final = (float(final)) * 60
final = round(final)
f[i] = final
else:
f[i] = int(val)
max_val = max(f, key = f.get) #max(f.items(), key=operator.itemgetter(1))[0]
min_val = min(f, key=f.get)#min(f.keys(), key=(lambda key: f[key]))#min(f.items(), key = operator.intemgetter(1))[0]
min_val = f[min_val]
max_val = f[max_val]
for i in f.keys():
if f[i] == max_val:
big.append(i)
if f[i] == min_val:
small.append(i)
return (self.questions_answered[big[0]], self.questions_answered[small[0]], big, small)
main function:
import clocky_classy as cm
import tkinter as tk
root = tk.Tk()
root.minsize(400,200)
my_clock = cm.Clock(root)
money_button = cm.ShowMoneyButton(root, my_clock)
root.protocol("WM_DELETE_WINDOW", my_clock.save)
timer = cm.Timer(root, 120, "2 min timer")
timer = cm.Timer(root, 1800, "30 min timer", counter = True)
my_clock.countup()
root.mainloop()
Does anybody know how I can implement circular progress bar on PyQt?
Also, I found an existing code:
http://sourceforge.net/projects/qroundprogressbar/
But, how it is in C++. How to use it for PyQt?
UPDATE: Using implementation of QRoundProgressBar below, I created a complete demo application with start button to demonstrate the QRoundProgressBar.
Save the QRoundProgressBar in circularprogressbar.py and create a new file in the same directory for the code below. Hope it helps others.
from circularprogressbar import QRoundProgressBar
import sys
from PyQt4.QtGui import *
from PyQt4 import QtCore, QtGui, Qt
from time import sleep
class TstWidget(QtGui.QWidget):
def __init__(self):
super(type(self), self).__init__()
self.bar = QRoundProgressBar()
self.bar.setFixedSize(300, 300)
self.bar.setDataPenWidth(3)
self.bar.setOutlinePenWidth(3)
self.bar.setDecimals(1)
self.bar.setFormat('%v | %p %')
# self.bar.resetFormat()
self.bar.setNullPosition(90)
self.bar.setBarStyle(QRoundProgressBar.StyleDonut)
self.bar.setDataColors([(0., QtGui.QColor.fromRgb(255,0,0)), (0.5, QtGui.QColor.fromRgb(255,255,0)), (1., QtGui.QColor.fromRgb(0,255,0))])
self.bar.setMaximun(100)
self.bar.setMinimun(0)
self.bar.setRange(0, 100)
self.bar.setValue(0)
button = QtGui.QPushButton("Start", self)
button.clicked.connect(self.on_start)
lay = QtGui.QVBoxLayout()
lay.addWidget(button)
lay.addWidget(self.bar)
self.setLayout(lay)
self.myLongTask = TaskThread()
self.myLongTask.notifyProgress.connect(self.on_progress)
def on_start(self):
self.myLongTask.start()
def on_progress(self, i):
self.bar.setValue(i)
class TaskThread(QtCore.QThread):
notifyProgress = QtCore.pyqtSignal(int)
def run(self):
for i in range(101):
self.notifyProgress.emit(i)
sleep(0.1)
def main():
app = QtGui.QApplication(sys.argv)
ex = TstWidget()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I have ported QRoundProgressBar on PyQt (and fixed some minor bugs):
from PyQt4 import QtCore, QtGui, Qt
class QRoundProgressBar(QtGui.QWidget):
StyleDonut = 1
StylePie = 2
StyleLine = 3
PositionLeft = 180
PositionTop = 90
PositionRight = 0
PositionBottom = -90
UF_VALUE = 1
UF_PERCENT = 2
UF_MAX = 4
def __init__(self):
super().__init__()
self.min = 0
self.max = 100
self.value = 25
self.nullPosition = self.PositionTop
self.barStyle = self.StyleDonut
self.outlinePenWidth =1
self.dataPenWidth = 1
self.rebuildBrush = False
self.format = "%p%"
self.decimals = 1
self.updateFlags = self.UF_PERCENT
self.gradientData = []
self.donutThicknessRatio = 0.75
def setRange(self, min, max):
self.min = min
self.max = max
if self.max < self.min:
self.max, self.min = self.min, self.max
if self.value < self.min:
self.value = self.min
elif self.value > self.max:
self.value = self.max
if not self.gradientData:
self.rebuildBrush = True
self.update()
def setMinimun(self, min):
self.setRange(min, self.max)
def setMaximun(self, max):
self.setRange(self.min, max)
def setValue(self, val):
if self.value != val:
if val < self.min:
self.value = self.min
elif val > self.max:
self.value = self.max
else:
self.value = val
self.update()
def setNullPosition(self, position):
if position != self.nullPosition:
self.nullPosition = position
if not self.gradientData:
self.rebuildBrush = True
self.update()
def setBarStyle(self, style):
if style != self.barStyle:
self.barStyle = style
self.update()
def setOutlinePenWidth(self, penWidth):
if penWidth != self.outlinePenWidth:
self.outlinePenWidth = penWidth
self.update()
def setDataPenWidth(self, penWidth):
if penWidth != self.dataPenWidth:
self.dataPenWidth = penWidth
self.update()
def setDataColors(self, stopPoints):
if stopPoints != self.gradientData:
self.gradientData = stopPoints
self.rebuildBrush = True
self.update()
def setFormat(self, format):
if format != self.format:
self.format = format
self.valueFormatChanged()
def resetFormat(self):
self.format = ''
self.valueFormatChanged()
def setDecimals(self, count):
if count >= 0 and count != self.decimals:
self.decimals = count
self.valueFormatChanged()
def setDonutThicknessRatio(self, val):
self.donutThicknessRatio = max(0., min(val, 1.))
self.update()
def paintEvent(self, event):
outerRadius = min(self.width(), self.height())
baseRect = QtCore.QRectF(1, 1, outerRadius-2, outerRadius-2)
buffer = QtGui.QImage(outerRadius, outerRadius, QtGui.QImage.Format_ARGB32)
buffer.fill(0)
p = QtGui.QPainter(buffer)
p.setRenderHint(QtGui.QPainter.Antialiasing)
# data brush
self.rebuildDataBrushIfNeeded()
# background
self.drawBackground(p, buffer.rect())
# base circle
self.drawBase(p, baseRect)
# data circle
arcStep = 360.0 / (self.max - self.min) * self.value
self.drawValue(p, baseRect, self.value, arcStep)
# center circle
innerRect, innerRadius = self.calculateInnerRect(baseRect, outerRadius)
self.drawInnerBackground(p, innerRect)
# text
self.drawText(p, innerRect, innerRadius, self.value)
# finally draw the bar
p.end()
painter = QtGui.QPainter(self)
painter.drawImage(0, 0, buffer)
def drawBackground(self, p, baseRect):
p.fillRect(baseRect, self.palette().background())
def drawBase(self, p, baseRect):
bs = self.barStyle
if bs == self.StyleDonut:
p.setPen(QtGui.QPen(self.palette().shadow().color(), self.outlinePenWidth))
p.setBrush(self.palette().base())
p.drawEllipse(baseRect)
elif bs == self.StylePie:
p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
p.setBrush(self.palette().base())
p.drawEllipse(baseRect)
elif bs == self.StyleLine:
p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
p.setBrush(Qt.Qt.NoBrush)
p.drawEllipse(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2))
def drawValue(self, p, baseRect, value, arcLength):
# nothing to draw
if value == self.min:
return
# for Line style
if self.barStyle == self.StyleLine:
p.setPen(QtGui.QPen(self.palette().highlight().color(), self.dataPenWidth))
p.setBrush(Qt.Qt.NoBrush)
p.drawArc(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2),
self.nullPosition * 16,
-arcLength * 16)
return
# for Pie and Donut styles
dataPath = QtGui.QPainterPath()
dataPath.setFillRule(Qt.Qt.WindingFill)
# pie segment outer
dataPath.moveTo(baseRect.center())
dataPath.arcTo(baseRect, self.nullPosition, -arcLength)
dataPath.lineTo(baseRect.center())
p.setBrush(self.palette().highlight())
p.setPen(QtGui.QPen(self.palette().shadow().color(), self.dataPenWidth))
p.drawPath(dataPath)
def calculateInnerRect(self, baseRect, outerRadius):
# for Line style
if self.barStyle == self.StyleLine:
innerRadius = outerRadius - self.outlinePenWidth
else: # for Pie and Donut styles
innerRadius = outerRadius * self.donutThicknessRatio
delta = (outerRadius - innerRadius) / 2.
innerRect = QtCore.QRectF(delta, delta, innerRadius, innerRadius)
return innerRect, innerRadius
def drawInnerBackground(self, p, innerRect):
if self.barStyle == self.StyleDonut:
p.setBrush(self.palette().alternateBase())
cmod = p.compositionMode()
p.setCompositionMode(QtGui.QPainter.CompositionMode_Source)
p.drawEllipse(innerRect)
p.setCompositionMode(cmod)
def drawText(self, p, innerRect, innerRadius, value):
if not self.format:
return
text = self.valueToText(value)
# !!! to revise
f = self.font()
# f.setPixelSize(innerRadius * max(0.05, (0.35 - self.decimals * 0.08)))
f.setPixelSize(innerRadius * 1.8 / len(text))
p.setFont(f)
textRect = innerRect
p.setPen(self.palette().text().color())
p.drawText(textRect, Qt.Qt.AlignCenter, text)
def valueToText(self, value):
textToDraw = self.format
format_string = '{' + ':.{}f'.format(self.decimals) + '}'
if self.updateFlags & self.UF_VALUE:
textToDraw = textToDraw.replace("%v", format_string.format(value))
if self.updateFlags & self.UF_PERCENT:
percent = (value - self.min) / (self.max - self.min) * 100.0
textToDraw = textToDraw.replace("%p", format_string.format(percent))
if self.updateFlags & self.UF_MAX:
m = self.max - self.min + 1
textToDraw = textToDraw.replace("%m", format_string.format(m))
return textToDraw
def valueFormatChanged(self):
self.updateFlags = 0;
if "%v" in self.format:
self.updateFlags |= self.UF_VALUE
if "%p" in self.format:
self.updateFlags |= self.UF_PERCENT
if "%m" in self.format:
self.updateFlags |= self.UF_MAX
self.update()
def rebuildDataBrushIfNeeded(self):
if self.rebuildBrush:
self.rebuildBrush = False
dataBrush = QtGui.QConicalGradient()
dataBrush.setCenter(0.5,0.5)
dataBrush.setCoordinateMode(QtGui.QGradient.StretchToDeviceMode)
for pos, color in self.gradientData:
dataBrush.setColorAt(1.0 - pos, color)
# angle
dataBrush.setAngle(self.nullPosition)
p = self.palette()
p.setBrush(QtGui.QPalette.Highlight, dataBrush)
self.setPalette(p)
Usage example:
class TstWidget(QtGui.QWidget):
def __init__(self):
super(type(self), self).__init__()
self.bar = QRoundProgressBar()
self.bar.setFixedSize(300, 300)
self.bar.setDataPenWidth(3)
self.bar.setOutlinePenWidth(3)
self.bar.setDonutThicknessRatio(0.85)
self.bar.setDecimals(1)
self.bar.setFormat('%v | %p %')
# self.bar.resetFormat()
self.bar.setNullPosition(90)
self.bar.setBarStyle(QRoundProgressBar.StyleDonut)
self.bar.setDataColors([(0., QtGui.QColor.fromRgb(255,0,0)), (0.5, QtGui.QColor.fromRgb(255,255,0)), (1., QtGui.QColor.fromRgb(0,255,0))])
self.bar.setRange(0, 300)
self.bar.setValue(260)
lay = QtGui.QVBoxLayout()
lay.addWidget(self.bar)
self.setLayout(lay)
#Alexandro, thank you for the code. It works nicely.
I found that it's best to set the min and max values as floats instead of int
self.min = 0.
self.max = 100.
self.value = 25.
Otherwise, the values won't update properly in python3 if the input value to valueToText() is also an integer, due to integer division
I have write this
class RoundProgress(QProgressBar):
def __init__(self,parent):
QProgressBar.__init__(self)
self.values = self.value()
self.values = (self.values*360)/100
self.parent = parent
self.setParent(parent)
self.n = self.value()
self.label = QLabel("<center>100%<center>")
self.label.setStyleSheet("color:red;")
self.label.setFont(QFont("courrier",math.sqrt(self.width())))
self.v = QVBoxLayout(self)
self.setLayout(self.v)
self.v.addWidget(self.label)
def setValue(self,n):
self.n = n
self.values = ((n*5650)/100)*(-1)
self.label.setText("<center>"+str(self.n)+"</center>")
def setNvalue(self,n):
self.n = n
self.values = ((n*5650)/100)*(-1)
self.label.setText("<center>"+str(self.n)+"</center>")
def paintEvent(self,event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
pen = QPen()
pen.setWidth(2)
pen.setColor(QColor("darkblue"))
painter.setPen(pen)
pen = QPen()
pen.setWidth(9)
pen.setColor(QColor("lightgrey"))
painter.setPen(pen)
painter.drawArc(5.1,5.1,self.width()-10,self.height()-10,1450,-5650)
#painter.drawEllipse(0,0,100,100)
painter.setBrush(QColor("lightblue"))
pen = QPen()
pen.setWidth(10)
pen.setColor(QColor("red"))
painter.setPen(pen)
painter.drawArc(5.1,5.1,self.width()-10,self.height()-10,1450,self.values)
self.update()
So I have a main window. I use setCentralWidget to set it to a class I made called mainWindowWidget to handle the UI.
I am now trying to add a graphics view widget I made to that but cannot seem to get anything to display. If I set the graphics view as the central widget I can see it and all my behaviour is working.
Do I need to do anything to get the graphics view to display within another widget?
Below are the sections of code I think are relevant to the question followed by the entire application. I am really new to PyQt and any guidance would be appreciated.
class mainWindowWidget(QtGui.QWidget):
grid = None
scene = None
def __init__(self):
self.initScene()
QtGui.QWidget.__init__(self)
def initScene(self):
self.grid = QtGui.QGridLayout()
'''Node Interface'''
self.scene = Scene(0, 0, 1280, 720, self)
self.view = QtGui.QGraphicsView()
self.view.setScene(self.scene)
self.grid.addWidget(self.view)
'''AttributeWindow'''
class MainWindowUi(QtGui.QMainWindow):
def __init__(self):
mainDataGraber = ind.dataGraber()
QtGui.QMainWindow.__init__(self)
self.setWindowTitle('RIS RIB Generator')
mainwindowwidget = mainWindowWidget()
self.setCentralWidget(mainwindowwidget)
This is the main GUI file for the application
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
This is the base py file for the GUI
Todo list
-----------------
- Pop up menu for adding new Nodes
- node connectivity
- create data structure for storing
"""
#import code mods
import sys
import uuid
import gtk, pygtk
from PyQt4 import QtGui, QtCore
from array import *
#import StyleMod
import RRG_NodeInterfaceGUIStyle as ns
import RRG_importNodeData as ind
"""
Base class for a node. Contains all the inilization, drawing, and containing inputs and outputs
"""
class node(QtGui.QGraphicsRectItem):
nid = 0
width = ns.nodeWidth
height = ns.nodeHeight
color = ns.nodeBackgroundColor
alpha = ns.nodeBackgroundAlpha
x = 90
y = 60
inputs=[]
outputs=[]
viewObj = None
isNode = True
scene = None
def __init__(self, n_x, n_y, n_width,n_height, n_scene):
QtGui.QGraphicsRectItem.__init__(self, n_x, n_y, n_width, n_height)
self.width = n_width
self.height = n_height
self.x = n_x
self.y = n_y
self.scene = n_scene
self.nid = uuid.uuid4()
print(self.nid)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.iniNodeData()
def mousePressEvent(self, e):
print("Square got mouse press event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
a = []
self.scene.selectedNodes = a
self.scene.selectedNodes.append(self)
self.scene.selectedNodeID = self.nid
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
def mouseReleaseEvent(self, e):
#print("Square got mouse release event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsRectItem.mouseReleaseEvent(self, e)
"""
This is where inputs and outputs will be created based on node type
"""
def iniNodeData(self):
print('making node data')
for j in range(5):
this = self
x = input(this,0, 0+(j*10),self.scene)
self.inputs.append(x)
for k in range(5):
this = self
x = output(this, self.width-10, 0+(k*10),self.scene)
self.outputs.append(x)
def mouseMoveEvent(self, event):
self.scene.updateConnections()
QtGui.QGraphicsRectItem.mouseMoveEvent(self, event)
def nid(self):
return self.nid
"""
Nodes will evaluate from the last node to the first node, therefore inputs are evaluted
"""
class input(QtGui.QGraphicsRectItem):
currentConnectedNode = None
currentConnectedOutput = None
parentNode = None
width = 10
height = 10
x = 1
y = 1
color = 1
drawItem = None
isOutput = False
isNode = False
scene = None
points = []
line = None
def __init__(self, pnode, posX, posY, n_scene):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
self.scene = n_scene
QtGui.QGraphicsRectItem.__init__(self, self.x+self.parentNode.x, self.y+self.parentNode.y, self.width, self.height, self.parentNode)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
'''
This will handel connections. It determins if a connection is allowed aswell as creates them
'''
def mousePressEvent(self, e):
#print("Square got mouse press event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
emptyArray = []
if len(self.scene.selectedNodes) > 0 or len(self.scene.selectedNodes) > 1:
a = self.scene.selectedNodes[0]
if a.isNode == False:
if a.parentNode != self.parentNode:
if a.isOutput == True and self.isOutput == False:
print('Output and Input! line test runnin....')
currentConnectedOutput = a
currentConnectedNode = a.parentNode
if self.line != None:
self.line = None
self.scene.addConnection(self, a)
elif a.isOutput == True and self.isOutput == True:
print('Output and Output')
elif a.isOutput == False and self.isOutput == False:
print('Input and Input')
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
self.scene.selectedNodes.append(self)
else:
self.scene.selectedNodes.append(self)
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
'''
Output value from a node
'''
class output(QtGui.QGraphicsRectItem):
parentNode = None
width = 10
height = 10
x = 0
y = 0
isOutput = True
isNode = False
scene = None
def __init__(self, pnode, posX, posY, n_scene):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
self.scene = n_scene
QtGui.QGraphicsRectItem.__init__(self, self.x+self.parentNode.x, self.y+self.parentNode.y, self.width, self.height, self.parentNode)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
'''
This will handel connections. It determins if a connection is allowed aswell as creates them
'''
def mousePressEvent(self, e):
#print("Square got mouse press event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
emptyArray = []
if len(self.scene.selectedNodes) > 0 or len(self.scene.selectedNodes) > 1:
a = self.scene.selectedNodes[0]
if a.isNode == False:
if a.parentNode != self.parentNode:
if a.isOutput == False and self.isOutput == True:
print('Input and Output!')
a.currentConnectedOutput = self
a.currentConnectedNode = self.parentNode
elif a.isOutput == True and self.isOutput == False:
print('Output and Input!')
elif a.isOutput == True and self.isOutput == True:
print('Output and Output')
elif a.isOutput == False and self.isOutput == False:
print('Input and Input')
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
self.scene.selectedNodes.append(self)
else:
self.scene.selectedNodes.append(self)
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
class connection(QtGui.QGraphicsLineItem):
usedNodeIDs = []
inputItem = None
outputItem = None
x1 = 0.0
y1 = 0.0
x2 = 0.0
y2 = 0.0
nid = None
scene = None
def __init__(self, n_inputItem, n_outputItemm, n_scene):
self.inputItem = n_inputItem
self.outputItem = n_outputItemm
self.usedNodeIDs.append(self.inputItem.parentNode.nid)
self.usedNodeIDs.append(self.outputItem.parentNode.nid)
self.updatePos()
QtGui.QGraphicsLineItem.__init__(self, self.x1, self.y1, self.x2, self.y2)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
self.scene = n_scene
self.nid = uuid.uuid4()
def update(self):
self.updatePos()
self.setLine(self.x1, self.y1, self.x2, self.y2)
def updatePos(self):
scenePosInput = QtGui.QGraphicsItem.pos(self.inputItem)
scenePosOutput = QtGui.QGraphicsItem.pos(self.outputItem)
scenePosInputNode = QtGui.QGraphicsItem.pos(self.inputItem.parentNode)
scenePosOutputNode = QtGui.QGraphicsItem.pos(self.outputItem.parentNode)
self.x1 = (scenePosInputNode.x()+self.inputItem.parentNode.x)+(scenePosInput.x()+self.inputItem.x) + ns.inputWidth/2
self.y1 = (scenePosInputNode.y()+self.inputItem.parentNode.y)+(scenePosInput.y()+self.inputItem.y) + ns.inputHeight/2
self.x2 = (scenePosOutputNode.x()+self.outputItem.parentNode.x)+(scenePosOutput.x()+self.outputItem.x) + ns.outputWidth/2
self.y2 = (scenePosOutputNode.y()+self.outputItem.parentNode.y)+(scenePosOutput.y()+self.outputItem.y) + ns.outputHeight/2
def mousePressEvent(self, e):
self.scene.selectedNodeID = self.nid
QtGui.QGraphicsLineItem.mousePressEvent(self, e)
'''
Check Click events on the scene Object
Also Stores the node data
'''
class Scene(QtGui.QGraphicsScene):
nodes = []
connections = []
selectedNodeID = None
def __init__(self, x, y, w, h, p):
super(Scene, self).__init__()
self.width = w
self.height = h
def mousePressEvent(self, e):
#print("Scene got mouse press event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsScene.mousePressEvent(self, e)
def mouseReleaseEvent(self, e):
#print("Scene got mouse release event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsScene.mouseReleaseEvent(self, e)
def dragMoveEvent(self, e):
QtGui.QGraphicsScene.dragMoveEvent(self, e)
def updateConnections(self):
for connect in self.connections:
connect.update()
def addNode(self):
newNode = node(250,250,100,150, self)
self.addItem(newNode)
self.nodes.append(newNode)
def addPatternNode(self):
newNode = node(250,250,100,150, self)
self.addItem(newNode)
self.nodes.append(newNode)
def addConnection(self, n_inputItem, n_outputItem):
newConnection = connection(n_inputItem, n_outputItem, self)
self.addItem(newConnection)
self.connections.append(newConnection)
def keyPressEvent(self, e):
#Delete a node after it have been clicked on
#Use the node ID as the unique ID of the node to delete
if e.key() == QtCore.Qt.Key_Delete or e.key() == QtCore.Qt.Key_Backspace:
#If nothing selected
if self.selectedNodeID != None:
isConnection = False
for j in range(len(self.connections)):
if self.connections[j].nid == self.selectedNodeID:
isConnection = True
self.removeItem(self.connections[j])
self.connections.remove(self.connections[j])
if isConnection != True:
#first remove connections
rmItem = False
connectionsToRemove = []
for connect in self.connections:
rmItem = False
for nid in connect.usedNodeIDs:
if nid == self.selectedNodeID:
if rmItem == False:
connectionsToRemove.append(connect)
rmItem = True
for removeThis in connectionsToRemove:
self.connections.remove(removeThis)
self.removeItem(removeThis)
#now remove the nodes
for j in range(len(self.nodes)):
print(self.nodes[j].nid)
#figure out which node in our master node list must be deleted
if self.nodes[j].nid == self.selectedNodeID:
self.removeItem(self.nodes[j])
self.nodes.remove(self.nodes[j])
self.selectedNodeID = None
class mainWindowWidget(QtGui.QWidget):
grid = None
scene = None
def __init__(self):
self.initScene()
QtGui.QWidget.__init__(self)
def initScene(self):
self.grid = QtGui.QGridLayout()
'''Node Interface'''
self.scene = Scene(0, 0, 1280, 720, self)
self.view = QtGui.QGraphicsView()
self.view.setScene(self.scene)
self.grid.addWidget(self.view)
'''AttributeWindow'''
class MainWindowUi(QtGui.QMainWindow):
def __init__(self):
mainDataGraber = ind.dataGraber()
QtGui.QMainWindow.__init__(self)
self.setWindowTitle('RIS RIB Generator')
mainwindowwidget = mainWindowWidget()
self.setCentralWidget(mainwindowwidget)
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close)
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'New Node', self)
newNodeAction.setStatusTip('Add a blank node')
newNodeAction.triggered.connect(mainwindowwidget.scene.addPatternNode)
nodeCreationActions = []
for nodeType in mainDataGraber.abstractNodeObjects:
nodeName = nodeType.nName
nodeType = nodeType.nType
#nodeStatusTip = nodeType.nhelp
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), nodeName, self)
newNodeAction.setStatusTip('nodeType.nhelp')
if nodeType == 'pattern':
newNodeAction.triggered.connect(mainwindowwidget.scene.addPatternNode)
nodeCreationActions.append(newNodeAction)
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'New Node', self)
newNodeAction.setStatusTip('Add a blank node')
newNodeAction.triggered.connect(mainwindowwidget.scene.addPatternNode)
self.statusBar()
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newNodeAction)
nodeMenu = menubar.addMenu('&Nodes')
for action in nodeCreationActions:
nodeMenu.addAction(action)
fileMenu.addAction(exitAction)
'''
Start Point
'''
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindowUi()
win.show()
sys.exit(app.exec_())
Your issue is that you don't specify a parent for the QGridLayout (in the mainWindowWidget class), so it isn't attached to a widget. This results in the layout (and all widgets contained within it) not being visible. Adding a parent to the layout reveals a second problem in that you try and do things with the QWidget before calling __init__.
The corrected code is thus:
class mainWindowWidget(QtGui.QWidget):
grid = None
scene = None
def __init__(self):
QtGui.QWidget.__init__(self)
self.initScene()
def initScene(self):
self.grid = QtGui.QGridLayout(self)
'''Node Interface'''
self.scene = Scene(0, 0, 1280, 720, self)
self.view = QtGui.QGraphicsView()
self.view.setScene(self.scene)
self.grid.addWidget(self.view)
Note: For future questions requiring debugging help, please make a minimilistic example that is runnable. Don't just dump 90% of your code in a stack overflow post. It's not fun trying to dig through random code trying to cut out the missing imports so that it still reproduces the problem (fortunately it wasn't too difficult in this case). See How to create a Minimal, Complete, and Verifiable example.
Note 2: Why are you importing pygtk into a qt app?
I am creating a small drawing application from a python book, "wxPython in Action", and it uses self.GetClientSize() to get the size of a window. For some reason this is return (0, 0) for me instead of the expected value (800, 600).
The program crashes when wx.EmptyBitmap is called with 0, 0 as its parameters. If I put
wx.EmptyBitmap(800, 600) the entire program runs fine, minus resizing.
Here is the relevant method
def InitBuffer(self):
size = self.GetClientSizeTuple()
print size
sys.exit(1)
self.buffer = wx.EmptyBitmap(size.width, size.height)
dc = wx.BufferedDC(None, self.buffer)
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
self.DrawLines(dc)
self.reInitBuffer = False
And this is the complete code
#!/usr/bin/arch -i386 /usr/bin/python2.6 -tt
import sys
import wx
class SketchWindow(wx.Window):
def __init__(self, parent, ID):
wx.Window.__init__(self, parent, ID)
self.SetBackgroundColour("White")
self.color = "Black"
self.thickness = 1
self.pen = wx.Pen(self.color, self.thickness, wx.SOLID)
self.lines = []
self.curLine = []
self.pos = (0, 0)
self.InitBuffer()
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_IDLE, self.OnIdle)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def InitBuffer(self):
size = self.GetClientSizeTuple()
print size
sys.exit(1)
self.buffer = wx.EmptyBitmap(size.width, size.height)
dc = wx.BufferedDC(None, self.buffer)
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
self.DrawLines(dc)
self.reInitBuffer = False
def GetLinesData(self):
return self.lines[:]
def SetLinesData(self, lines):
self.lines = lines[:]
self.InitBuffer()
self.Refresh()
def OnLeftDown(self, event):
self.curLine = []
self.pos = event.GetPositionTuple()
self.CaptureMouse()
def OnLeftUp(self, event):
if self.HasCapture():
self.lines.append((self.color, self.thickness, self.curLine))
self.curLine = []
self.ReleaseMouse()
def OnMotion(self, event):
if event.Dragging() and event.LeftIsDown():
dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
self.drawMotion(dc, event)
event.Skip()
def drawMotion(self, dc, event):
dc.SetPen(self.pen)
newPos = event.GetPositionTuple()
coords = self.pos + newPos
self.curLine.append(coords)
dc.DrawLine(*coords)
self.pos = newPos
def OnSize(self, event):
self.reInitBuffer = True
def OnIdle(self, event):
if self.reInitBuffer:
self.InitBuffer()
self.Refresh(False)
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self, self.buffer)
def DrawLines(self, dc):
for (colour, thickness, line) in self.lines:
pen = wx.Pen(colour, thickness, wx.SOLID)
dc.SetPen(pen)
for coord in line:
dc.DrawLine(*coord)
def SetColor(self, color):
self.color = color
self.pen = wx.Pen(self.color, self.thickness, wx.SOLID)
def GetColor(self):
return self.color
def SetThickness(self, thickness):
self.thickness = thickness
self.pen = wx.Pen(self.color, self.thickness, wx.SOLID)
class SketchFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, 'Sketch Frame', size=(800, 600))
self.sketch = SketchWindow(self, -1)
def main():
app = wx.PySimpleApp()
frame = SketchFrame(None)
frame.Show(True)
app.MainLoop()
if __name__ == '__main__':
main()
It's because you're calling GetSize in the __init__() method - the window isn't fully created until this method has completed. Thus, it hasn't has its width and height set properly.
You could use wx.CallAfter/CallLater to postpone the calling of this function until window creation has fully completed.
I don't know if there is a better solution, but the problem is that when the object was being initialized it didn't have a parent yet, so it didn't know what size it should be. Thus it was width 0 and height 0. However, it needed to initialize the buffer. What I did to fix this was
if size == (0, 0):
size.width = 1
size.height = 1
Once it is added to the frame it gets a new size and the buffer is resized. So I guess that works!
I suppose another solution would be to pass a size parameter to the init method, but i'd prefer not to have to do that if it is not required.
Please post other solutions if you have them =)