Tkinter window does not load completely - python

I'm trying to have a progress window which shows the progress, alongside having tasks happening in the background. Everything works as expected, except the window partially loads on to the screen (how much of it does depends on every run). Here is the relevant part of the code:
def loading(): #Displays loading progress while setting up the exam
global load, progress
load = Toplevel()
load.title("Loading")
load.attributes('-topmost', True)
load.overrideredirect(True)
lab = Label(load, text = ("Preparing Your Exam, Please Wait!\nPlease DO NOT Open any Other Window.\n"
+"Doing so may lead to immidiate Termination."))
lab.grid(row = 0, column = 1, padx = 20, pady = 20)
progress=Progressbar(load,orient=HORIZONTAL,length=200,mode='determinate')
progress.grid(row = 1, column = 1, padx = 20, pady = 20)
log = Label(load, image = logo)
log.image = logo
log.grid(row = 0, column = 0, rowspan = 2, padx = 20, pady = 20)
w_req, h_req = load.winfo_width(), load.winfo_height()
w_form = load.winfo_rootx() - load.winfo_x()
w = w_req + w_form*2
h = h_req + (load.winfo_rooty() - load.winfo_y()) + w_form
x = (load.winfo_screenwidth() // 2) - (w // 2)
y = (load.winfo_screenheight() // 2) - (h // 2)
load.geometry(f'{w_req}x{h_req}+{x}+{y}')
Here's what happens after calling loading:
loading()
conv_th = Thread(target = Convert).start()
The Convert function converts and processes images, I'm not sharing that because it might not be relevant.
I far as I think, it might be because it is not getting enough time to load completely, but I couldn't really figure out what could be causing the program to behave this way. Any help will be appreciated!
Update: This behavior is seen even if conv_th = Thread(target = Convert).start() is omitted, implying that there could be a problem within the loading() function.

So, I ended up solving the problem myself. I'm telling the reason that I think is most probable, please correct me if the reason that I give is incorrect or there is another solution for this.
This part of the code,
w_req, h_req = load.winfo_width(), load.winfo_height()
w_form = load.winfo_rootx() - load.winfo_x()
w = w_req + w_form*2
h = h_req + (load.winfo_rooty() - load.winfo_y()) + w_form
x = (load.winfo_screenwidth() // 2) - (w // 2)
y = (load.winfo_screenheight() // 2) - (h // 2)
was being executed too early, before the window could actually load itself up completely, and hence whatever amount of it got loaded before this, was taken as the dimensions and then the values were set.
Adding the command load.update_idletasks() before the above part of the code resolved the problem. Thanks #martineau, your comment was really helpful in figuring this out.

Related

How to undo when using QScintilla setText?

Let me start by posting some little helper functions I'll use to formulate my questions:
import textwrap
import sys
from pathlib import Path
from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import * # noqa
def set_style(sci):
# Set default font
sci.font = QFont()
sci.font.setFamily('Consolas')
sci.font.setFixedPitch(True)
sci.font.setPointSize(8)
sci.font.setBold(True)
sci.setFont(sci.font)
sci.setMarginsFont(sci.font)
sci.setUtf8(True)
# Set paper
sci.setPaper(QColor(39, 40, 34))
# Set margin defaults
fontmetrics = QFontMetrics(sci.font)
sci.setMarginsFont(sci.font)
sci.setMarginWidth(0, fontmetrics.width("000") + 6)
sci.setMarginLineNumbers(0, True)
sci.setMarginsForegroundColor(QColor(128, 128, 128))
sci.setMarginsBackgroundColor(QColor(39, 40, 34))
sci.setMarginType(1, sci.SymbolMargin)
sci.setMarginWidth(1, 12)
# Set indentation defaults
sci.setIndentationsUseTabs(False)
sci.setIndentationWidth(4)
sci.setBackspaceUnindents(True)
sci.setIndentationGuides(True)
sci.setFoldMarginColors(QColor(39, 40, 34), QColor(39, 40, 34))
# Set caret defaults
sci.setCaretForegroundColor(QColor(247, 247, 241))
sci.setCaretWidth(2)
# Set edge defaults
sci.setEdgeColumn(80)
sci.setEdgeColor(QColor(221, 221, 221))
sci.setEdgeMode(sci.EdgeLine)
# Set folding defaults (http://www.scintilla.org/ScintillaDoc.html#Folding)
sci.setFolding(QsciScintilla.CircledFoldStyle)
# Set wrapping
sci.setWrapMode(sci.WrapNone)
# Set selection color defaults
sci.setSelectionBackgroundColor(QColor(61, 61, 52))
sci.resetSelectionForegroundColor()
# Set scrollwidth defaults
sci.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)
# Current line visible with special background color
sci.setCaretLineBackgroundColor(QColor(255, 255, 224))
# Set multiselection defaults
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
sci.SendScintilla(QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)
def set_state1(sci):
sci.clear_selections()
base = "line{} state1"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(0, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 0:
view.set_selection(region)
else:
view.add_selection(region)
def set_state2(sci):
base = "line{} state2"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(1, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 1:
view.set_selection(region)
else:
view.add_selection(region)
class Editor(QsciScintilla):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
set_style(self)
def clear_selections(self):
sci = self
sci.SendScintilla(sci.SCI_CLEARSELECTIONS)
def set_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_SETSELECTION, r[1], r[0])
def add_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_ADDSELECTION, r[1], r[0])
def sel(self):
sci = self
regions = []
for i in range(sci.SendScintilla(sci.SCI_GETSELECTIONS)):
regions.append(
sci.SendScintilla(sci.SCI_GETSELECTIONNSTART, i),
sci.SendScintilla(sci.SCI_GETSELECTIONNEND, i)
)
return sorted(regions)
I've got a couple of questions actually:
Question 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
I'll get this (you can see the question in the below snapshot):
Question 2)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
set_state2(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
How can I modify the code so I'll be able to restore state1 when pressing ctrl+z?
Right now when using ctrl+z you won't be able to get state1:
mainly because how setText behaves:
Replaces all of the current text with text. Note that the undo/redo
history is cleared by this function.
I've already tried some of the functions posted in the undo and redo docs but no luck so far.
For instance, one of my attempts has been first selecting all text and then using replaceSelectedText and finally restoring the selections from the previous state manually, the result was ugly (i don't want the editor scrolling messing up when undoing/redoing)... Basically, I'd like to get the same feeling than SublimeText.
Btw, this is a little minimal example but in the real-case I'll be accumulating a bunch of operations without committing to scintilla very often... that's why I'm interested to figure out how to rollback to a previous state when using the undoable setText... Said otherwise, i'd like to avoid using Scintilla functions such as insertAt, replaceSelectedText or similars... as I'm using python string builtin functions to modify the buffer internally.
EDIT:
I'm pretty sure beginUndoAction & endUndoAction won't help me to answer question2 but... what about SCI_ADDUNDOACTION? Although the docs are pretty confusing though... :/
Question 1:
The last selection added is automatically set as the Main selection. To remove it, add line sci.SendScintilla(sci.SCI_SETMAINSELECTION, -1) at the end of the set_state1 function.
Question 2:
The way you described it by storing the selections, using the replaceSelectedText, and then using setCursorPosition / reselecting all selections and setFirstVisibleLine to restore the scroll position is one way to go.
Looking at the C++ source of the setText function:
// Set the given text.
void QsciScintilla::setText(const QString &text)
{
bool ro = ensureRW();
SendScintilla(SCI_SETTEXT, ScintillaBytesConstData(textAsBytes(text)));
SendScintilla(SCI_EMPTYUNDOBUFFER);
setReadOnly(ro);
}
You could try setting the text using sci.SendScintilla(sci.SCI_SETTEXT, b"some text"), which doesn't reset the undo/redo buffer.

How to properly create function that moves a shape using Zelle's 'graphics.py' program?

Question 17 of Ch. 6 in Zelle's 'Programming Python' books asks for a function that takes two arguments: 'shape', and 'newCenter', which will then move, or re-draw, an existing object based on the new points provided by a mouse-click.
I've been able to figure out how to do it if I add two more parameters to the function, "myX", and "myY", so I can then subtract the difference for the move method. What is eluding me is how to perform the same calculations using just the two parameters specified above. Here is my code:
def moveTo(shape, newCenter, myX, myY):
myShape = shape
myNewX = newCenter.getX()
myNewY = newCenter.getY()
myXUpd = myNewX - myX
myYUpd = myNewY - myY
myShape.move(myXUpd, myYUpd)
return myNewX, myNewY
def main():
win = GraphWin("My Graph Win", 500, 500)
win.setBackground("white")
win.setCoords(0, 0, 10, 10)
Text(Point(5, 8.5), "Please click 10 times.").draw(win)
myPoint = win.getMouse()
myX = myPoint.getX()
myY = myPoint.getY()
myCircle = Circle(myPoint, 2)
myCircle.draw(win)
for x in range(1, 10):
myNewPoint = win.getMouse()
myX, myY = moveTo(myCircle, myNewPoint, myX, myY)
win.close()
Also, any general tips for streamlining, style, or structure are also appreciated as I'm a rather new Python developer.
Thanks!
I figured this out on my own, so anyone going through the Zelle "Python Programming" book can learn from my experience. I just pulled the X and Y out of the shape object I was passing in, thus saving myself having to pass them in again separately. Here is the updated code with some of the fat trimmed down.
def moveTo(shape, newCenter):
oldCenter = shape.getCenter()
myOldX, myOldY = oldCenter.getX(), oldCenter.getY()
myNewX, myNewY = newCenter.getX(), newCenter.getY()
moveX = myNewX - myOldX
moveY = myNewY - myOldY
shape.move(moveX, moveY)
return shape
def main():
win = GraphWin("My Graph Win", 500, 500)
win.setBackground("white")
win.setCoords(0, 0, 10, 10)
Text(Point(5, 8.5), "Please click 10 times.").draw(win)
myPoint = win.getMouse()
myX, myY = myPoint.getX(), myPoint.getY()
myShape = Circle(myPoint, 2)
myShape.draw(win)
for x in range(1, 10):
newCenter = win.getMouse()
myShape = moveTo(myShape, newCenter)
win.close()

Openstack Permission denied when I generate a picture by python

I use a program which can generate a picture. I saved it by
img.save("/usr/lib/python2.6/site-packages/openstackdashboard/static/dashboard/img/validate.jpeg")
return strs # strs is picture's data
Everything goes right when run it alone . But " IOError " occured when I call it by
from .auth_code import Create_Validate_Code
auth_code_str = Create_Validate_Code()
And horizon says " [Errno 13] Permission denied: '/usr/lib/python2.6/site-packages/openstack-dashboard/static/dashboard/img/validate.jpeg' ". Could someone help me ? Thanks a lot .
This is all code to create a picture
#!/usr/bin/env python
import random
import Image, ImageDraw, ImageFont, ImageFilter
_letter_cases = "1234567890"
_upper_cases = _letter_cases.upper()
_numbers = ''.join(map(str, range(3, 10)))
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
fontType="/usr/share/fonts/lohit-tamil/Lohit-Tamil.ttf"
def create_lines(draw,n_line,width,height):
line_num = random.randint(n_line[0],n_line[1])
for i in range(line_num):
begin = (random.randint(0, width), random.randint(0, height))
end = (random.randint(0, width), random.randint(0, height))
draw.line([begin, end], fill=(0, 0, 0))
def create_points(draw,point_chance,width,height):
chance = min(100, max(0, int(point_chance)))
for w in xrange(width):
for h in xrange(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
def create_strs(draw,chars,length,font_type, font_size,width,height,fg_color):
c_chars = random.sample(chars, length)
strs = ' %s ' % ' '.join(c_chars)
font = ImageFont.truetype(font_type, font_size)
font_width, font_height = font.getsize(strs)
draw.text(((width - font_width) / 3, (height - font_height) / 3),strs, font=font, fill=fg_color)
return ''.join(c_chars)
def Create_Validate_Code(size=(120, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type=fontType,
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance = 2):
width, height = size
img = Image.new(mode, size, bg_color)
draw = ImageDraw.Draw(img)
if draw_lines:
create_lines(draw,n_line,width,height)
if draw_points:
create_points(draw,point_chance,width,height)
strs = create_strs(draw,chars,length,font_type, font_size,width,height,fg_color)
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params)
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
img.save("/usr/lib/python2.6/site-packages/openstack-dashboard/static/dashboard/img/validate.jpeg")
return strs
The code to create and save the file is inside the function Create_Validate_Code. In your initial version, you never call that function anywhere. Therefore, it never tries to create and save the file, so it never fails.
When you add this:
from .auth_code import Create_Validate_Code
auth_code_str = Create_Validate_Code()
… now you're calling the function. So now it fails. It has nothing whatsoever to do with the third-party module you're using; you could do the same thing with just this:
Create_Validate_Code()
Meanwhile, the reason that creating the file fails is that you don't have write access to directories in the middle of your system's site-packages. This is by design. This is why operating systems have permissions in the first place—to stop some buggy or malicious code run as a normal user from screwing up programs and data needed by the entire system.
Create the file somewhere you do have access to, like some place in your home directory, or the temporary directory, or whatever's appropriate to whatever you're trying to do, and the problem will go away.
Have you tried running the final app as Administrator/root? That usually fixes any "Permission denied" errors while programming.
You shouldn't save data deep within your Python installation. It's really bad practice, which is why the OS is preventing you from doing it. Save the picture somewhere in your home folder.

Creating a mouse over text box in tkinter

I'm trying to implement system where when the user points to an object, a text box appears with certain information which I haven't implemented yet, then disappears when they move their mouse away. I'm trying to do that by binding the < Enter > and < Leave > commands, but nothing happens when I run the following code, except that in the terminal it says that destroy requires two arguments, so I know it is calling the functions.
from tkinter import *
xhig, yhig = 425,325
bkgnclr = '#070707'
currentmouseoverevent = ''
c = Canvas(master, width=xhig*2, height=yhig*2, bg=bkgnclr, cursor = 'crosshair',)
def mouseovertext(event):
mouseover = "Jack"
currentmouseoverevent = event
c.create_rectangle(bbox=(event.x,event.y, (event.x + 5), (event.y +len(mouseover)*5)),outline="white", fill=bkgnclr, width= len(mouseover))
c.create_text(position=(event.x,event.y),text=mouseover, fill="white", currentmouseoverevent=event)
def closemouseover(x):
c.destroy(currentmouseoverevent)
c.bind("<Enter>", mouseovertext)
c.bind("<Leave>", closemouseover)
What arguments does destroy take, and why is the rectangle not being created?
A bounding box (bbox) in tkinter is a 4-tuple which stores the bounds of the rectangle. You are only passing in the mouse location, which is a 2-tuple.
Also, you are never actually assigning to the variable "currentmouseoverevent" before using it in the code you show, so your closemouseover function will fail.
The corrected code is as follows.
It turns out I was calling bbox wrong. Instead of passing the coords as a tuple, I should have passed them as the first four agrguments of create_rectangle. c.destroy is only for objects like canvas, entry or textbox, instead I used c.delete for deleting items, and used the event number returned by c.create_rectangle and c.create_text.
from tkinter import *
xhig, yhig = 425,325
bkgnclr = '#070707'
currentmouseoverevent = ['','']
c = Canvas(master, width=xhig*2, height=yhig*2, bg=bkgnclr, cursor = 'crosshair',)
def mouseovertext(event):
mouseover = "Jack"
if currentmouseoverevent[0] != '':
closemouseover()
currentmouseoverevent[0]=''
return
currentmouseoverevent[0] = c.create_rectangle(event.x,event.y, (event.x + 5), (event.y +len(mouseover)*5),outline="white", fill=bkgnclr, width= len(mouseover))
currentmouseoverevent[1] = c.create_text(event.x,event.y,text=mouseover, fill="white", currentmouseoverevent=event,anchor=NW)
def closemouseover(x):
c.delete(currentmouseoverevent[0])
c.delete(currentmouseoverevent[1])
c.bind("<Button-3", mouseovertext)

Python: Easygui Window Size

I'm creating a GUI for my python script in EasyGUI. Does anyone know of a way I can change the default Window size? The default is far too big.
Thanks for your help.
Before install open easygui.py and edit element you want to have another size. (You can also reinstall and override it)
This will be line like
boxRoot.minsize(root_width, root_height)
in the corresponfing function.
To be more flexible add width and height as parameters to the function.
def __choicebox(msg
, title
, choices
, width_ = 480
, height_ = 320
):
....
root_width = width_
root_height = height_
root_xpos = int((screen_width * 0.1))
root_ypos = int((screen_height * 0.05))
boxRoot.title(title)
boxRoot.iconname('Dialog')
rootWindowPosition = "+0+0"
boxRoot.geometry(rootWindowPosition)
boxRoot.expand=NO
boxRoot.minsize(root_width, root_height)
I pseudo-pulled the code from GitHub to mess with the choicebox because of the size problem and I did the following .... I don't know if this is a good fix or not but it seemed to work to make at least the width of the screen variable according to the length of msg, title, and list of choices:
...
choices = list(choices[:])
add lines
choiceLen = len(max(choices,key=len))
titleLen = len(title)
msgLen = len(msg)
maxLen = max(choiceLen,titleLen,msgLen)
...
root_width = int((screen_width * 0.8))
root_width = int(maxLen * 10.)

Categories

Resources